<?php
/**
 * Mailview
 * http://code.google.com/p/mailview/
 * Martin Tournoij <martin@arp242.net>
 * Mailview is free for any use, there are no restrictions.
 */

include('imap.php');

/**
 * The main class: This handles all the basic template and login stuff.
 */
class Mailview2
{
	# Configuration
	private $config = array();
	private $vars = array();

	function __construct()
	{
		$this->time = array('start' => microtime(True));

		# Error handeling
		error_reporting(E_ALL);
		ini_set('display_errors', 'on');
		ini_set('html_errors', 'off');

		define('VERSION', '2.2');

		# Required extensions
		$required = array('pcre', 'session', 'imap', 'iconv', 'mbstring');
		foreach ($required as $r)
		{
			if (!extension_loaded($r))
				trigger_error("Required extension $r not loaded.", E_USER_ERROR);
		}

		# Load config
		$this->LoadConfig();

		# Set up some defaults
		date_default_timezone_set($this->config['timezone']);
		iconv_set_encoding('output_encoding', $this->config['charset']);
		iconv_set_encoding('internal_encoding', $this->config['charset']);
		mb_internal_encoding($this->config['charset']);

		if (isset($_POST['cookie']))
			$a = setcookie('mailview2_save', (int) $_POST['cookie'], time() + 3600 * 24 * 365 * 10 , '/');

		$this->InitAuth();

		# Not logged in
		if (!$this->IsLoggedIn())
		{
			# User filled in form and we try to login 
			if (isset($_POST['username']) or $this->config['autologin'])
			{
				$_SESSION['ref'] = '';
				$this->Login();
			}
			else
			{
				$this->Template(array(
					'config' => $this->config,
					'nomenu' => True,
					'accounts' => $this->accountnames,
					'curaccount' => $this->curaccount,
				), Null, 'login.html');
			}
		}
	}

	/**
	 *
	 */
	function LoadConfig()
	{
		# Load configuration
		if (!is_file('config/default.ini') and !is_link('config/default.ini'))
			trigger_error('Unable to find config/default.ini', E_USER_ERROR);

		$default = parse_ini_file('config/default.ini');
		if (is_file('config/config.ini') or is_link('config/config.ini'))
		{
			# Check if we already stored the config in the session.
			if (isset($_SESSION['config']))
			{
				$this->config = $_SESSION['config'];
				return;
			}
			$accounts = parse_ini_file('config/config.ini', True);
			$noaccounts = False;
			foreach ($accounts as $k => &$v)
			{
				# There is just one account and no sections
				if (!is_array($v))
				{
					$this->config = array_merge($default, $accounts);
					$this->accountnames = array('Default');
					$this->curaccount = 'Default';
					$noaccounts = True;
					break;
				}

				$v = array_merge($default, $v);
			}
		}

		if (!$noaccounts)
		{
			$this->accountnames = array_keys($accounts);
			if (isset($_GET['account']) and isset($accounts["{$_GET['account']}"]))
			{
				$this->config = $accounts["{$_GET['account']}"];
				$this->curaccount = $_GET['account'];
			}
			else
			{
				$this->config = array_shift($accounts);
				$this->curaccount = $this->accountnames['0'];
			}
		}
	}

	/**
	 * Set up some stuff for authentication
	 */
	function InitAuth()
	{
		if (session_id() === '' and !headers_sent())
		{
			session_name('mailview2');

			if (isset($_COOKIE['mailview2_save']))
				session_set_cookie_params(3600 * 24 * $_COOKIE['mailview2_save'], '/');
			else
				session_set_cookie_params(0, '/');

			session_start();
			$_SESSION['version'] = VERSION;
		}

		// Logout users from previous versions.
		if (isset($_SESSION['config']) and !isset($_SESSION['version']))
		{
			$this->Logout();
			header('Location: index.php');
			exit(0);
		}
	}

	/**
	 * Check if the user is logged in.
	 *
	 * @return bool
	 */
	function IsLoggedIn()
	{
		if (isset($_SESSION['username']))
			return True;
		else
			return False;
	}

	/**
	 *
	 */
	function Login()
	{
		#if ($this->config['defaultserveronly'] and $this->config['server'] != $_POST['server'])
		#	trigger_error('defaultserveronly is enabled and server is not the same as specified in the config file.', E_USER_ERROR);

		# Override default config values with the ones from the form
		$this->config = array_merge($this->config, $_POST);

		# Check connection/authentication
		$check = Mailview\TestImap($this->config);
		if (gettype($check) === 'string')
		{
			print "There was an error:<br />\n $check<br />\n";
			print '<a href="index.php">Try again</a>';
			exit(0);
		}
		else
		{
			# Actually leveraging IDLE connection is not particuarly easy, you can't 
			# just use 's = socket(); date = fread(socket);' in Javascript ...
			# TODO Look at websockets, socket.io, or something like that...
			$_SESSION['idle'] = $check;
		}

		$_SESSION['username'] = $this->config['username'];
		$_SESSION['config'] = $this->config;
	}

	function Logout()
	{
		$p = session_get_cookie_params();
		setcookie(session_name(), '', 0, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
		session_write_close();
		unset($_SESSION);
	}

	/**
	 * Display a template with twig.
	 *
	 * @param array $vars Array with variables to be used inside the template
	 *
	 * @param string $templatefile Name of template to use (with extension) default is 
	 * to use PHP_SELF with .php replaced by .html
	 *
	 * @return void
	 */
	function Template($vars=array(), $link=Null, $templatefile=Null)
	{
		$this->time['templatestart'] = microtime(True);
		require_once('contrib/twig/lib/Twig/Autoloader.php');

		if ($templatefile === Null)
			$templatefile = str_replace('.php', '.html', basename($_SERVER['PHP_SELF']));

		Twig_Autoloader::register();
		$loader = new Twig_Loader_Filesystem('templates/' . $this->config['template']);
		$twig = new Twig_Environment($loader, array(
			#'cache' => '/var/tmp/twigcache'
		));
		$twig->addFilter('substr', new Twig_Filter_Function('mb_substr'));

		$template = $twig->loadTemplate($templatefile);

		# Add a bunch fo default variables that are always available
		$this->vars = array_merge(array(
			'phpself' => $_SERVER['PHP_SELF'],
			'version' => VERSION,
			'tpldir' => 'templates/' . $this->config['template'],
			'template' => $templatefile,
			'title' => $this->config['title'],
		), $vars);
		if (isset($_SESSION['mailbox']))
			$this->vars['mailbox'] = $_SESSION['mailbox'];

		if (isset($_SESSION['config']['autoexpunge']))
			$this->vars['autoexpunge'] = $_SESSION['config']['autoexpunge'];

		header('Content-Type: text/html; charset=utf-8');
		header('Cache-Control: no-cache, must-revalidate');
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');

		# Get list of folders for the menu, close the link
		if ($link !== Null)
		{
			$this->Menu($link);
			Mailview\imap_close($link);
		}

		$template->display($this->vars);
		$this->time['templateend'] = microtime(True);

		# For testing
		#print "<!--\n";
		#foreach ($this->time as $k => $t)
		#	printf("%s: %.2f seconds from start\n", $k, $t - $this->time['start']);
		#print '-->';

		exit(0);
	}

	/**
	 * Get the menu structure
	 */
	function Menu($link)
	{
		$this->time['menustart'] = microtime(True);

		if ($_SESSION['config']['allfolders'])
			$this->vars['menu'] = Mailview\imap_list($link, $_SESSION['ref'], '*');
		else
			$this->vars['menu'] = Mailview\imap_lsub($link, $_SESSION['ref'], '*');
		sort($this->vars['menu']);

		foreach ($this->vars['menu'] as &$m)
		{
			$info = Mailview\imap_status($link, $m, SA_MESSAGES + SA_UNSEEN);

			# Subfolders
			$pn = str_replace($_SESSION['ref'], '', $m);
			$ex = explode($_SESSION['config']['separator'], $pn);
			while (count($ex) > 1)
			{
				$p = array_shift($ex);
				$pn = '&nbsp;&nbsp;' . str_replace($p . $_SESSION['config']['separator'],  '', $m);
			}

			$m = array(
				'prettyname' => str_replace($_SESSION['ref'], '', $pn),
				'name' => str_replace($_SESSION['ref'], '', $m),
				'new' => $info->unseen,
				'total' => $info->messages,
			);
		}
	
		$this->time['menuend'] = microtime(True);
	}
}

/**
 * Like grep: Return all lines that are matched by $regex
 *
 * @param string $regex Regular expresion to match lines.
 * @param string $str String to match
 * @param bool $remove Also remove matching lines
 * @param reference $removed This is $str with matching lines removed, only 
 * set if $remove=True
 *
 */
function Grep($regex, $str, $remove=False, &$removed=Null)
{
	$str = explode("\n", $str);

	$removed = '';
	$matches = array();
	foreach ($str as $line)
	{
		$c = preg_match($regex, $line);
		if ($c > 0)
			$matches[] = "$line\n";
		elseif ($remove)
			$removed .= "$line\n";
	}

	return $matches;
}

/**
 * print_r, but more useful.
 * I use this during development sometimes, not used in the actual program.
 */
function printr($arr)
{
	print '<pre>';
	print htmlentities(print_r($arr, True));
	print '</pre>';
}

/**
 * Redirect (reload) a page according to the Post/Redirect/Get principle
 * N.B. This function exits the script!
 *
 * @param array $get Array with GET variables.
 * @param string $dest Filename, defaults to PHP_SELF
 */
function Redirect($get='', $dest='')
{
  if ($get !== '')
  {
    $vars = '?';
    foreach ($get as $k => $v)
      $vars .= sprintf('%s=%s&', urlencode($k), urlencode($v));
    $vars = trim($vars, '&');
  }
  else
    $vars = '';

  if ($dest === '')
    $dest = $_SERVER['PHP_SELF'];

  header(sprintf('Location: %s%s', $dest, $vars));

  exit(0);
}

?>
